#include "EditDistanceCalculator.hpp"

#include "Library.hpp"

const double EditDistanceCalculator::COST_OF_REMOVING_NODE=50;

EditDistanceCalculator::EditDistanceCalculator(const std::vector<ConfSet>& first_path,const std::vector<ConfSet>& second_path)
{
	std::vector<std::vector<double>> cost_array;
	for(UINT32 i=0;i<=first_path.size();i++)
	{
		cost_array.push_back(std::vector<double>());
		_my_decisions.push_back(std::vector<editing_options>());
		for(UINT32 j=0;j<=second_path.size();j++)
		{
			cost_array[i].push_back(0);
			_my_decisions[i].push_back(SWITCH);
			if(i==0)
			{
				_my_decisions[i][j]=LEFT;
				cost_array[i][j]=j;
			}
			if(j==0)
			{
				_my_decisions[i][j]=UP;
				cost_array[i][j]=i;
			}
			if( (i!=0 && j!=0) || (i==0 && j==0))
			{
				_my_decisions[i][j]=SWITCH;
			}
		}
	}
	for(UINT32 i=1;i<=first_path.size();i++)
	{
		for(UINT32 j=1;j<=second_path.size();j++)
		{
			UINT32 minimum_cost=cost_array[i-1][j-1]+Library::configuration_distance(first_path[i-1],second_path[j-1]);
			_my_decisions[i][j]=SWITCH;
			if(minimum_cost>(cost_array[i][j-1]+COST_OF_REMOVING_NODE))
			{
				_my_decisions[i][j]=LEFT;
				minimum_cost=cost_array[i][j-1]+COST_OF_REMOVING_NODE;
			}
			if(minimum_cost>(cost_array[i-1][j]+COST_OF_REMOVING_NODE))
			{
				_my_decisions[i][j]=UP;
				minimum_cost=cost_array[i-1][j]+COST_OF_REMOVING_NODE;
			}
			cost_array[i][j]=minimum_cost;	
		}
	}
/*	print_costs(cost_array);
	print_decisions();
	printf("The distance is: %f\n",cost_array[first_path.size()-1][second_path.size()-1]); */
}

std::string EditDistanceCalculator::decision_to_string(editing_options e) const
{
	if(e==UP)
	{
		return "UP  ";
	}
	if(e==LEFT)
	{
		return "LEFT";
	}
	return "SWAP";
}

void EditDistanceCalculator::print_costs(const std::vector<std::vector<double>>& costs) const
{
	for(UINT32 i=0;i<costs.size();i++)
	{
		for(UINT32 j=0;j<costs[i].size();j++)
		{
			printf("%f ",costs[i][j]);
		}
		printf("\n");
	}
}



void EditDistanceCalculator::print_decisions() const
{
	for(UINT32 i=0;i<_my_decisions.size();i++)
	{
		for(UINT32 j=0;j<_my_decisions[i].size();j++)
		{
			printf("%s ",decision_to_string(_my_decisions[i][j]).c_str());
		}
		printf("\n");
	}
}

std::vector<editing_options> EditDistanceCalculator::get_shortest_path() const
{
	std::vector<editing_options> shortest_path;
	INT32 i=_my_decisions.size()-1;
	INT32 j=_my_decisions[0].size()-1;
	INT32 k;
	if(i>j)
		k=i;
	else
		k=j;
	for(;k>1 && (i>=0 && j>=0);k--)
	{
		if(k!=(_my_decisions.size()-1) )
		{
			shortest_path.push_back(_my_decisions[i][j]);
		}
		if(_my_decisions[i][j]==SWITCH)
		{
			j--;
			i--;
			continue;
		}
		if(_my_decisions[i][j]==LEFT)
		{
			j--;
			continue;
		}
		if(_my_decisions[i][j]==UP)
		{
			i--;
			continue;
		}
	}
/*	printf("Printing shortest path:\n");
	for(UINT32 k=0;k<shortest_path.size();k++)
	{
		printf("%s ",decision_to_string(shortest_path[k]).c_str());
	}
	printf("\n"); */
	return shortest_path;
}

std::vector<std::vector<UINT32>> EditDistanceCalculator::get_connections_to_try() const
{
	std::vector<editing_options> shortest_path=get_shortest_path();
	UINT32 i=_my_decisions.size()-3; //start from the second to last;
	UINT32 j=_my_decisions[0].size()-3;
	std::vector<std::vector<UINT32>> connections_to_try;
	for(UINT32 k=0;k<shortest_path.size();k++)
	{
		if(shortest_path[k]==SWITCH)
		{
			std::vector<UINT32> switch_pair;
			switch_pair.push_back(i);
			switch_pair.push_back(j);
			connections_to_try.push_back(switch_pair);
			i--;
			j--;
		}
		if(shortest_path[k]==LEFT)
		{
			std::vector<UINT32> switch_with_higher;
			switch_with_higher.push_back(i);
			switch_with_higher.push_back(j+1);
			connections_to_try.push_back(switch_with_higher);
			std::vector<UINT32> switch_with_lower;
			switch_with_lower.push_back(i);
			switch_with_lower.push_back(j-1);
			connections_to_try.push_back(switch_with_lower);
			j--;
		}
		if(shortest_path[k]==UP)
		{
			std::vector<UINT32> switch_with_higher;
			switch_with_higher.push_back(i+1);
			switch_with_higher.push_back(j);
			connections_to_try.push_back(switch_with_higher);
			std::vector<UINT32> switch_with_lower;
			switch_with_lower.push_back(i-1);
			switch_with_lower.push_back(j);
			connections_to_try.push_back(switch_with_lower);
			i--;
		}
	}
/*	printf("Printing connections to try:\n");
	for(UINT32 k=0;k<connections_to_try.size();k++)
	{
		printf("%d-%d\n",connections_to_try[k][0],connections_to_try[k][1]);
	}
	printf("\n"); */
	return connections_to_try;
}